﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Web.UI;
using System.Web.UI.WebControls;

[assembly: WebResource("Comm.PageGrid.js", "text/javascript")]

namespace Comm
{
    /// <summary>
    /// 分页表格控件
    /// </summary>
    [PersistChildren(false)]
    [ParseChildren(true, "Columns")]
    [DefaultProperty("Columns")]
    [DefaultEvent("Invalidate")]
    //[System.Diagnostics.DebuggerStepThrough]
    public class PageGrid : DataBoundControl, IPostBackEventHandler {
        #region 构造
        PageGridCache cache;

        public PageGrid() {
            Columns = new PageGridColumnCollection();
            EnableCache = true;
            CompressCache = true;
            cache = new PageGridCache(this);
        }
        #endregion

        #region 事件
        /// <summary>
        /// 数据无效时触发，如排序，翻页或调整分页大小
        /// </summary>
        [Description("数据无效时触发，如排序，翻页或调整分页大小")]
        public event EventHandler Invalidate;
        bool _invalidated = false;

        protected virtual void OnInvalidate() {
            _invalidated = true;

            if (Invalidate != null) {
                Invalidate(this, EventArgs.Empty);
            }
        }

        /// <summary>
        /// 选择值的变更事件
        /// </summary>
        [Description("选择值的变更事件")]
        public event EventHandler SelectedValueChanged;

        /// <summary>
        /// 自定义命令事件
        /// </summary>
        public event EventHandler<CommandEventArgs> Command;

        /// <summary>
        /// 获取自定义事件的前端调用脚本，生成类似 __doPostback()方法的引用
        /// </summary>
        /// <param name="commandName"></param>
        /// <param name="commandArgument"></param>
        /// <returns></returns>
        public string GetCommandEventClientScript(string commandName, object commandArgument) {
            return Page.ClientScript.GetPostBackEventReference(this, string.Format("Command{0}:{1}{2}"
                , commandName == null ? 0 : commandName.Length
                , commandName, commandArgument));
        }

        /// <summary>
        /// 获取当前页缓存的数据源
        /// </summary>
        /// <returns></returns>
        public T GetCachedDataSource<T>() {
            return (T)cache.Pop();
        }
        /// <summary>
        /// 重建选中行
        /// </summary>
        /// <param name="action">重建回调</param>
        /// <typeparam name="RowType">数据行类型</typeparam>
        public void RebuildSelectedRow<RowType>(Action<RowType> action) {
            if (action == null)
                throw new ArgumentNullException("action");

            var source = this.DataSource ?? GetCachedDataSource<object>();

            if (source != null && !string.IsNullOrEmpty(KeyName)) {
                if (source is IListSource)
                    source = ((IListSource)source).GetList();
                else if (!(source is IEnumerable))
                    throw new InvalidOperationException("不支持的数据源处理：" + this.DataSource.GetType());

                foreach (var row in (IEnumerable)source) {
                    if (SelectedValue == DataBinder.Eval(row, KeyName, "{0}")) {
                        action((RowType)row);
                        break;
                    }
                }
            }
        }
        #endregion

        #region 属性
        [Description("列可动态调整顺序")]
        [DefaultValue(false)]
        public bool Adjustable {
            get;
            set;
        }
        [Description("主键属性名称")]
        [DefaultValue(null)]
        public string KeyName {
            get;
            set;
        }
        [Description("列集合")]
        [PersistenceMode(PersistenceMode.InnerDefaultProperty)]
        public PageGridColumnCollection Columns {
            get;
            private set;
        }

        [Description("扩展容器，用于添加详情等自定义内容")]
        [DefaultValue(null)]
        [PersistenceMode(PersistenceMode.InnerDefaultProperty)]
        [TemplateContainer(typeof(PageGridTemplateContainer))]
        public ITemplate ExtensionContainer {
            get;
            set;
        }

        /// <summary>
        /// 分页大小
        /// </summary>
        [Description("分页大小")]
        [DefaultValue(10)]
        public int PageSize {
            get {
                return (int)(ViewState["_PageSize"] ?? 10);
            }
            set {
                if (value < 0)
                    value = 1;
                ViewState["_PageSize"] = value;
            }
        }

        [Description("记录总数")]
        [DefaultValue(0)]
        [Browsable(false)]
        public int TotalRecords {
            get {
                return (int)(ViewState["_TotalRecords"] ?? 0);
            }
            set {
                if (value < 0)
                    throw new InvalidOperationException("总记录数不能小于0");
                ViewState["_TotalRecords"] = value;
            }
        }

        [Description("当前页")]
        [DefaultValue(1)]
        [Browsable(false)]
        public int CurrentPage {
            get {
                return (int)(ViewState["_CurrentPage"] ?? 1);
            }
            private set {
                ViewState["_CurrentPage"] = value;
            }
        }

        int SortColumnIndex {
            get {
                if (ViewState["_SortColumnIndex"] == null) {
                    var column = Columns.Find(c => c.IsDefaultSort);
                    if (column == null)
                        column = Columns.Find(c => c.Sortable);
                    if (column == null) {
                        return -1;
                    }
                    ViewState["_SortColumnIndex"] = Columns.IndexOf(column);
                }
                return (int)ViewState["_SortColumnIndex"];
            }
            set {
                ViewState["_SortColumnIndex"] = value;
            }
        }

        /// <summary>
        /// 排序列
        /// </summary>
        [Browsable(false)]
        public PageGridColumn SortColumn {
            get {
                if (SortColumnIndex >= 0 && SortColumnIndex < Columns.Count) {
                    return Columns[SortColumnIndex];
                }
                return Columns.Find(column => column.IsDefaultSort)
                    ?? Columns.Find(column => column.Sortable);
            }
        }

        /// <summary>
        /// 排序方式
        /// </summary>
        [Browsable(false)]
        public SortDirection SortType {
            get {
                return (SortDirection)(ViewState["_SortType"] ?? SortDirection.Ascending);
            }
            set {
                ViewState["_SortType"] = value;
            }
        }

        /// <summary>
        /// 是否启用缓存
        /// </summary>
        [Description("是否启用缓存")]
        [DefaultValue(true)]
        public bool EnableCache {
            get;
            set;
        }

        /// <summary>
        /// 是否压缩缓存
        /// </summary>
        [Description("是否压缩缓存")]
        [DefaultValue(true)]
        public bool CompressCache {
            get;
            set;
        }

        /// <summary>
        /// 选中的值
        /// </summary>
        [Browsable(false)]
        public string SelectedValue {
            get {
                return ViewState["_SelectedValue"] as string;
            }
            set {
                if (value == null) {
                    ViewState.Remove("_SelectedValue");
                }
                else {
                    ViewState["_SelectedValue"] = value;
                }
            }
        }

        /// <summary>
        /// 重置控件状态，通常是执行新查询时调用，重置包括定位至1页、清除选中项、清除总数 
        /// </summary>
        public void ResetState() {
            ViewState.Remove("_SelectedValue");
            ViewState.Remove("_TotalRecords");
            ViewState.Remove("_CurrentPage");

        }

        /// <summary>
        /// 绑定全量数据, 绑定后选择的值如果不在当前页中将会被清除，默认不选中首行
        /// </summary>
        /// <param name="fullSource"></param>
        public void BindFullDataTable(DataTable fullSource) {
            BindFullDataTable(fullSource, false);
        }

        /// <summary>
        /// 绑定全量数据, 绑定后选择的值如果不在当前页中将会被清除，指定是否自动选中首行
        /// </summary>
        /// <param name="fullSource"></param>
        /// <param name="autoSelectFirst">自动选中首行</param>
        public void BindFullDataTable(DataTable fullSource, bool autoSelectFirst) {
            if (fullSource == null) {
                this.TotalRecords = 0;
            }
            else {
                this.TotalRecords = fullSource.Rows.Count;
                DataRow[] pagedRows;
                if (this.SortColumn != null && !string.IsNullOrEmpty(SortColumn.Name)) {
                    var pagedData = fullSource.Select("", SortColumn.Name
                        + " " + (SortType == SortDirection.Ascending ? "ASC" : "DESC"));
                    pagedRows = pagedData.Skip((this.CurrentPage - 1) * PageSize).Take(PageSize).ToArray();
                }
                else {
                    pagedRows = fullSource.Select().Skip((this.CurrentPage - 1) * PageSize).Take(PageSize).ToArray();
                }
                fullSource = fullSource.Clone();
                Array.ForEach(pagedRows, row => fullSource.Rows.Add(row.ItemArray));

                if (autoSelectFirst && pagedRows.Length > 0 && !string.IsNullOrEmpty(KeyName)) {
                    SelectedValue = string.Format("{0}", pagedRows[0][KeyName]);
                }
            }
            this.DataSource = fullSource;
            DataBind();
        }

        #endregion

        #region 重写
        bool _isDataBound = false;
        /// <summary>
        /// 呈现前，如果数据源未给定，组指定了缓存，则从缓存中恢复数据源并绑定
        /// </summary>
        /// <param name="e"></param>
        protected override void OnPreRender(EventArgs e) {
            // 未绑定数据、且启用缓存、且数据没有失效（非设计时）
            if (!_isDataBound && EnableCache && !DesignMode) {
                if (!_invalidated)
                    this.DataSource = cache.Pop();
                this.DataBind();
            }
            base.OnPreRender(e);

            Page.ClientScript.RegisterClientScriptResource(typeof(PageGrid), "Comm.PageGrid.js");

            //if (Visible)
            //    ScriptManager.RegisterStartupScript(this, typeof(PageGrid), ClientID,string.Format("PageGrid.Initialize('{0}');", ClientID), true);
        }

        protected override void OnDataBound(EventArgs e) {
            base.OnDataBound(e);
            if (this.DataSource != null && EnableCache && !DesignMode) {
                cache.Push();
            }
            _isDataBound = true;
        }

        protected override HtmlTextWriterTag TagKey {
            get {
                return HtmlTextWriterTag.Table;
            }
        }

        public override void RenderControl(HtmlTextWriter writer) {
            CreateInformationBar(writer);

            base.AddAttributesToRender(writer);

            writer.AddAttribute(HtmlTextWriterAttribute.Cellspacing, "0");
            writer.AddAttribute(HtmlTextWriterAttribute.Cellpadding, "0");
            writer.AddAttribute(HtmlTextWriterAttribute.Class, "pg-grid");

            RenderBeginTag(writer);
            writer.Write(htmlBuffer.ToString());
            RenderEndTag(writer);
        }

        void CreateInformationBar(HtmlTextWriter writer) {
            writer.AddAttribute(HtmlTextWriterAttribute.Class, "pg-navigation");
            writer.RenderBeginTag(HtmlTextWriterTag.Div);

            writer.Write(@"总行数：<em>{0}</em> <i>|</i> 总页数：<em>{1}</em> <i>|</i>每页显示 <input id={2} name={2} value={3} onchange={4}> 行"
                , TotalRecords, (int)Math.Ceiling(TotalRecords * 1d / PageSize), UniqueID + "PageSize", PageSize
                , Page.ClientScript.GetPostBackEventReference(this, "PageSize"));

            #region 分页导航
            if (TotalRecords > 0) {
                writer.AddAttribute(HtmlTextWriterAttribute.Class, "pg-nav-pages");
                writer.RenderBeginTag(HtmlTextWriterTag.Span);

                writer.Write("<span class=\"pg-nav-first\" title=\"第一页\" onclick=\"{0}\">&nbsp;</span>",
                    Page.ClientScript.GetPostBackEventReference(this, "First"));
                writer.Write("<span class=\"pg-nav-prev\" title=\"上一页\" onclick=\"{0}\">&nbsp;</span>",
                    Page.ClientScript.GetPostBackEventReference(this, "Prev"));

                int totalPages = (int)Math.Ceiling(1d * TotalRecords / PageSize);
                int from, to, offset = 2;
                to = Math.Min(Math.Max(1 + offset, CurrentPage) + offset, totalPages);
                from = Math.Max(1, to - (2 * offset + 1));

                for (int i = from; i <= to; i++) {
                    writer.Write("<span class=\"{0}\" onclick=\"{1}\">{2}</span>"
                        , i != CurrentPage ? "pg-nav-goto" : "pg-nav-current"
                        , i != CurrentPage ? Page.ClientScript.GetPostBackEventReference(this, "Goto" + i) : null
                        , i);
                }

                writer.Write("<span class=\"pg-nav-next\" title=\"下一页\" onclick=\"{0}\">&nbsp;</span>",
                    Page.ClientScript.GetPostBackEventReference(this, "Next"));
                writer.Write("<span class=\"pg-nav-last\" title=\"下一页\" onclick=\"{0}\">&nbsp;</span>",
                    Page.ClientScript.GetPostBackEventReference(this, "Last"));

                writer.RenderEndTag();
            }
            #endregion

            writer.RenderEndTag();
        }

        void CreateTemplate(HtmlTextWriter writer, ITemplate template, object dataItem, int dataIndex, int displayIndex) {
            PageGridBindContext.PushDataBindingContext(Page, dataItem);
            PageGridTemplateContainer container = new PageGridTemplateContainer(dataItem, dataIndex, displayIndex);
            container.Page = Page;
            template.InstantiateIn(container);
            container.DataBind();
            PageGridBindContext.PopDataBindingContext(Page);
            container.RenderControl(writer);
        }

        /// <summary>
        /// 创建表头
        /// </summary>
        /// <param name="writer"></param>
        void CreateHeader(HtmlTextWriter writer) {
            writer.AddAttribute(HtmlTextWriterAttribute.Class, "pg-header");
            writer.RenderBeginTag(HtmlTextWriterTag.Tr);
            var columnIndex = 0;

            #region 选中行
            writer.AddAttribute(HtmlTextWriterAttribute.Id, "0");
            writer.AddAttribute(HtmlTextWriterAttribute.Class, "pg-toggler");
            //writer.AddAttribute(HtmlTextWriterAttribute.Onclick, "Comm.PageGrid.Toggle(\"" + this.ClientID + "\", this);");
            writer.RenderBeginTag(HtmlTextWriterTag.Td);
            writer.Write("<span>&nbsp;</span>");
            writer.RenderEndTag();
            #endregion

            foreach (var column in Columns) {
                writer.AddAttribute(HtmlTextWriterAttribute.Width,
                    column.Width.ToString());
                if (column.Sortable) {
                    writer.AddAttribute(HtmlTextWriterAttribute.Onclick
                        , Page.ClientScript.GetPostBackEventReference(this, "Sort" + columnIndex));
                }
                writer.AddAttribute(HtmlTextWriterAttribute.Id, (columnIndex + 1).ToString());
                column.ApplyCustomAttributes(writer);
                writer.RenderBeginTag(HtmlTextWriterTag.Td);
                // 未指定模板标题时显示文本
                if (column.TemplateCaption == null)
                    writer.WriteEncodedText(column.Caption ?? string.Empty);
                else
                    CreateTemplate(writer, column.TemplateCaption, column, columnIndex, columnIndex + 1);

                if (column == this.SortColumn) {
                    writer.AddAttribute(HtmlTextWriterAttribute.Class,
                        this.SortType == SortDirection.Ascending ? "pg-sort-asc" : "pg-sort-desc");
                    writer.RenderBeginTag(HtmlTextWriterTag.Span);
                    writer.RenderEndTag();
                }
                writer.RenderEndTag();
                columnIndex++;
            }
            writer.RenderEndTag();
        }

        /// <summary>
        /// 创建内容
        /// </summary>
        /// <param name="writer"></param>
        /// <param name="data"></param>
        void CreateContent(HtmlTextWriter writer, IEnumerable data) {
            var dataIndex = 0;
            if (data == null) {
                if (SelectedValue != null) {
                    SelectedValue = null;
                    if (SelectedValueChanged != null)
                        SelectedValueChanged(this, EventArgs.Empty);
                }
                return;
            }
            bool foundSelected = false;
            foreach (var item in data) {
                string cssClass = dataIndex % 2 == 0 ? "pg-data" : "pg-data-alter";
                if (!string.IsNullOrEmpty(this.KeyName)) {
                    var key = GetDataBinderData(item, KeyName, null);
                    foundSelected = foundSelected || (key != null && key.ToString() == SelectedValue);

                    if (key != null && key.ToString() == SelectedValue) {
                        cssClass += " pg-data-selected";
                    }
                }
                writer.AddAttribute(HtmlTextWriterAttribute.Class, cssClass);

                writer.RenderBeginTag(HtmlTextWriterTag.Tr);
                writer.RenderBeginTag(HtmlTextWriterTag.Td);
                writer.AddAttribute(HtmlTextWriterAttribute.Class, "pg-selection-icon");
                writer.RenderBeginTag(HtmlTextWriterTag.Span);
                writer.Write("&nbsp;");
                writer.RenderEndTag();//~Span
                writer.RenderEndTag();//~Td

                foreach (var column in Columns) {
                    if (column.ContentAttributes != null)
                        column.ContentAttributes.ApplyContentAttributes(writer);
                    writer.RenderBeginTag(HtmlTextWriterTag.Td);

                    if (column.TemplateContent == null) {
                        if (column.IsSelectionTrigger) {
                            if (string.IsNullOrEmpty(KeyName) && !DesignMode) {
                                throw new InvalidProgramException("给出列的SelectionTrigger时必须设置控件的KeyName(主键名)");
                            }
                            writer.AddAttribute(HtmlTextWriterAttribute.Href, "#");
                            writer.AddAttribute(HtmlTextWriterAttribute.Onclick,
                                Page.ClientScript.GetPostBackEventReference(this
                                    , "Select" + GetDataBinderData(item, KeyName, null)) + ";return false;");
                            writer.RenderBeginTag(HtmlTextWriterTag.A);
                        }
                        if (string.IsNullOrEmpty(column.Name)) {
                            writer.Write("Name或TemplateContent属性必需");
                        }
                        else {
                            writer.Write(GetDataBinderData(item, column.Name, column.Format) ?? string.Empty);
                        }
                        if (column.IsSelectionTrigger) {
                            writer.RenderEndTag();
                        }
                    }
                    else
                        CreateTemplate(writer, column.TemplateContent, item, dataIndex, dataIndex + 1);

                    writer.RenderEndTag();
                }

                writer.RenderEndTag();//~Tr

                #region Extension
                if (ExtensionContainer != null) {
                    writer.AddAttribute(HtmlTextWriterAttribute.Class, cssClass);
                    writer.RenderBeginTag(HtmlTextWriterTag.Tr);
                    writer.AddAttribute(HtmlTextWriterAttribute.Colspan, (Columns.Count + 1).ToString());
                    writer.RenderBeginTag(HtmlTextWriterTag.Td);
                    CreateTemplate(writer, ExtensionContainer, item, dataIndex, dataIndex + 1);
                    writer.RenderEndTag();//~Td
                    writer.RenderEndTag();//~Tr
                }
                #endregion
                dataIndex++;
                if (dataIndex >= PageSize)
                    break;
            }

            if (!foundSelected)
                if (SelectedValue != null) {
                    SelectedValue = null;
                    if (SelectedValueChanged != null)
                        SelectedValueChanged(this, EventArgs.Empty);
                }
        }

        object GetDataBinderData(object item, string name, string format) {
            try {
                if (!string.IsNullOrEmpty(format)) {
                    return DataBinder.Eval(item, name, format);
                }
                else {
                    return DataBinder.Eval(item, name);
                }
            }
            catch {
                if (DesignMode) {
                    return name;
                }
                throw;
            }
        }
        StringWriter htmlBuffer;
        protected override void PerformDataBinding(IEnumerable data) {
            htmlBuffer = new StringWriter();
            HtmlTextWriter writer = new HtmlTextWriter(htmlBuffer);

            CreateHeader(writer);
            CreateContent(writer, data);
        }
        #endregion

        public void RaisePostBackEvent(string eventArgument) {
            if (eventArgument.StartsWith("Sort")) {
                int index = int.Parse(eventArgument.Substring(4));
                SortType = index == SortColumnIndex
                    ? SortDirection.Ascending == SortType ? SortDirection.Descending : SortDirection.Ascending
                    : SortDirection.Ascending;
                SortColumnIndex = index;
                OnInvalidate();
            }
            else if (eventArgument == "PageSize") {
                int pageSize = PageSize;
                if (int.TryParse(Page.Request[UniqueID + "PageSize"], out pageSize) && pageSize > 0) {
                    PageSize = pageSize;
                    OnInvalidate();
                }
            }
            else if (eventArgument == "First") {
                CurrentPage = 1;
                OnInvalidate();
            }
            else if (eventArgument == "Prev") {
                CurrentPage = Math.Max(1, CurrentPage - 1);
                OnInvalidate();
            }
            else if (eventArgument == "Next") {
                CurrentPage = Math.Min((int)Math.Ceiling(1d * TotalRecords / PageSize), CurrentPage + 1);
                OnInvalidate();
            }
            else if (eventArgument == "Last") {
                CurrentPage = (int)Math.Ceiling(1d * TotalRecords / PageSize);
                OnInvalidate();
            }
            else if (eventArgument.StartsWith("Goto")) {
                CurrentPage = int.Parse(eventArgument.Substring(4));
                OnInvalidate();
            }
            else if (eventArgument.StartsWith("Select")) {
                SelectedValue = eventArgument.Substring(6);
                if (SelectedValueChanged != null)
                    SelectedValueChanged(this, EventArgs.Empty);
            }
            else if (Command != null) {
                //Command{0}:{1}{2}
                Match match = Regex.Match(eventArgument, @"Command(?'Length'\d+):(?'Arg'.*)");
                if (match.Success) {
                    var commandNameLength = int.Parse(match.Groups["Length"].Value);
                    var commandArguments = match.Groups["Arg"].Value;
                    Command(this, new CommandEventArgs(commandArguments.Substring(0, commandNameLength)
                        , commandArguments.Substring(commandNameLength)));
                }
            }
        }
    }

    /// <summary>
    /// 控件列
    /// </summary>
    [ParseChildren(true)]
    [DefaultProperty("TemplateContent")]
    [System.Diagnostics.DebuggerStepThrough]
    public class PageGridColumn : IAttributeAccessor {
        #region Content Attributes
        public class PageGridColumnContentAttributes : IAttributeAccessor {
            Dictionary<string, string> customAttributes = new Dictionary<string, string>(StringComparer.CurrentCultureIgnoreCase);

            [DefaultValue(null)]
            [Description("列样式类名")]
            public string CssClass {
                get {
                    return customAttributes["CssClass"];
                }
                set {
                    customAttributes["CssClass"] = value;
                }
            }

            [DefaultValue(null)]
            [Description("列样式")]
            public string Style {
                get {
                    return customAttributes["Style"];
                }
                set {
                    customAttributes["Style"] = value;
                }
            }

            [DefaultValue(null)]
            [Description("列样式")]
            public string Align {
                get {
                    return customAttributes["Align"];
                }
                set {
                    customAttributes["Align"] = value;
                }
            }

            [DefaultValue(null)]
            [Description("列样式")]
            public string VAlign {
                get {
                    return customAttributes["VAlign"];
                }
                set {
                    customAttributes["VAlign"] = value;
                }
            }

            string IAttributeAccessor.GetAttribute(string key) {
                return customAttributes[key];
            }

            void IAttributeAccessor.SetAttribute(string key, string value) {
                customAttributes[key] = value;
            }

            /// <summary>
            /// 将属性应用至输出流
            /// </summary>
            /// <param name="writer"></param>
            public void ApplyContentAttributes(HtmlTextWriter writer) {
                foreach (var attr in customAttributes) {
                    writer.AddAttribute(attr.Key, attr.Value);
                }
            }
        }
        #endregion
        public PageGridColumn() {
            Sortable = true;
            Name = string.Empty;
        }

        [DefaultValue(null)]
        [Description("列样式类名")]
        public string CssClass {
            get {
                return customAttributes["CssClass"];
            }
            set {
                customAttributes["CssClass"] = value;
            }
        }

        /// <summary>
        /// 当前列内容的属性
        /// </summary>
        [DefaultValue(null)]
        [Description("内容属性")]
        [PersistenceMode(PersistenceMode.InnerDefaultProperty)]
        public PageGridColumnContentAttributes ContentAttributes {
            get;
            set;
        }

        /// <summary>
        /// 列标题
        /// </summary>
        [DefaultValue(null)]
        [Description("列标题")]
        public string Caption {
            get;
            set;
        }

        [DefaultValue(null)]
        [Description("列样式")]
        public string Style {
            get {
                return customAttributes["Style"];
            }
            set {
                customAttributes["Style"] = value;
            }
        }

        [DefaultValue(null)]
        [Description("列样式")]
        public string Align {
            get {
                return customAttributes["Align"];
            }
            set {
                customAttributes["Align"] = value;
            }
        }

        [DefaultValue(null)]
        [Description("列样式")]
        public string VAlign {
            get {
                return customAttributes["VAlign"];
            }
            set {
                customAttributes["VAlign"] = value;
            }
        }

        /// <summary>
        /// 模板标题
        /// </summary>
        [DefaultValue(null)]
        [Description("模板标题")]
        [PersistenceMode(PersistenceMode.InnerDefaultProperty)]
        [TemplateContainer(typeof(PageGridTemplateContainer))]
        public ITemplate TemplateCaption {
            get;
            set;
        }

        /// <summary>
        /// 模板内容，如果未指定将默认展示列值
        /// </summary>
        [DefaultValue(null)]
        [Description("模板内容，如果未指定将默认展示列值")]
        [PersistenceMode(PersistenceMode.InnerDefaultProperty)]
        [TemplateContainer(typeof(PageGridTemplateContainer))]
        public ITemplate TemplateContent {
            get;
            set;
        }

        /// <summary>
        /// DataTable的列名或Entity的属性名称，排序时将传入此名称
        /// </summary>
        [DefaultValue("")]
        [Description("DataTable的列名或Entity的属性名称，排序时将传入此名称")]
        public string Name {
            get;
            set;
        }

        private string _format;
        [DefaultValue(null)]
        [Description("数据格式化字符串，如日期:yyyy-MM-dd")]
        public string Format {
            get {
                return _format;
            }
            set {
                if (value != null) {
                    if (!Regex.IsMatch(value, @"[^\{]\{")) {
                        value = "{0:" + value + "}";
                    }
                }
                _format = value;
            }
        }

        /// <summary>
        /// 是否支持排序
        /// </summary>
        [DefaultValue(true)]
        [Description("是否支持排序")]
        public bool Sortable {
            get;
            set;
        }

        /// <summary>
        /// 列宽，默认空
        /// </summary>
        [DefaultValue("")]
        [Description("列宽，默认空")]
        public Unit Width {
            get;
            set;
        }

        /// <summary>
        /// 是否默认排序
        /// </summary>
        [DefaultValue(false)]
        [Description("是否默认排序")]
        public bool IsDefaultSort {
            get;
            set;
        }

        /// <summary>
        /// 是否选择触发器列
        /// </summary>
        [DefaultValue(false)]
        [Description("是否选择触发器列，为true时，指定了控件的KeyName且未列自定义TemplateContent，则生成超链接，以选中当前行")]
        public bool IsSelectionTrigger {
            get;
            set;
        }

        /// <summary>
        /// 分组合并时参考的列，列号从1开始
        /// </summary>
        [DefaultValue(-1)]
        [Description("分组合并时参考的列，列号从1开始")]
        public int Grouping {
            get {
                if (string.IsNullOrEmpty(customAttributes["data-grouping"])) {
                    return -1;
                }
                return int.Parse(customAttributes["data-grouping"]);
            }
            set {
                if (value < -1)
                    throw new IndexOutOfRangeException("Grouping");
                customAttributes["data-grouping"] = value.ToString();
            }
        }

        #region Customer Attributes
        /// <summary>
        /// 输出自定义属性
        /// </summary>
        /// <param name="writer"></param>
        internal void ApplyCustomAttributes(HtmlTextWriter writer) {
            foreach (var attr in customAttributes) {
                writer.AddAttribute(attr.Key, attr.Value);
            }
        }
        Dictionary<string, string> customAttributes = new Dictionary<string, string>(StringComparer.CurrentCultureIgnoreCase);
        string IAttributeAccessor.GetAttribute(string key) {
            return customAttributes[key];
        }

        void IAttributeAccessor.SetAttribute(string key, string value) {
            customAttributes[key] = value;
        }

        #endregion
    }

    /// <summary>
    /// 列集合
    /// </summary>
    [System.Diagnostics.DebuggerStepThrough]
    public class PageGridColumnCollection : List<PageGridColumn> {
        /// <summary>
        /// 查询指定名称的列
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public PageGridColumn Find(string name) {
            return this.Find(column => column.Name == name);
        }
    }

    /// <summary>
    /// 模板容器
    /// </summary>
    [System.Diagnostics.DebuggerStepThrough]
    public class PageGridTemplateContainer : TemplateControl, IDataItemContainer {
        public PageGridTemplateContainer(object dataItem, int dataItemIndex, int displayIndex) {
            this.DataItem = dataItem;
            this.DataItemIndex = dataItemIndex;
            this.DisplayIndex = displayIndex;
        }

        public object DataItem {
            get;
            private set;
        }

        public int DataItemIndex {
            get;
            private set;
        }

        public int DisplayIndex {
            get;
            private set;
        }
    }

    /// <summary>
    /// 表格数据源缓存, 对数据放入ViewState中存储
    /// </summary>
    [System.Diagnostics.DebuggerStepThrough]
    class PageGridCache {
        private PageGrid pageGrid;
        bool isChanged = true;
        object cachedDataSource = null;

        public PageGridCache(PageGrid pageGrid) {
            this.pageGrid = pageGrid;
        }

        /// <summary>
        /// 将数据源进行缓存
        /// </summary>
        public void Push() {
            isChanged = true;

            if (pageGrid.DataSource == null) {
                return;
            }
            using (StringWriter writer = new StringWriter()) {
                LosFormatter formatter = new LosFormatter();
                formatter.Serialize(writer, pageGrid.DataSource);

                ScriptManager.RegisterHiddenField(pageGrid, "__PGCACHE_" + pageGrid.UniqueID,
                    pageGrid.CompressCache ? Compress(writer.ToString()) : writer.ToString());
            };
        }

        /// <summary>
        /// 恢复缓存数据源
        /// </summary>
        public object Pop() {
            if (isChanged) {
                var cache = pageGrid.Page.Request["__PGCACHE_" + pageGrid.UniqueID];
                if (!string.IsNullOrEmpty(cache)) {
                    LosFormatter formatter = new LosFormatter();
                    cachedDataSource = formatter.Deserialize(
                        pageGrid.CompressCache ? Decompress(cache) : cache);
                }
                isChanged = false;
            }
            return cachedDataSource;
        }

        #region Compression
        string Compress(string plainText) {
            byte[] source = Encoding.Default.GetBytes(plainText);
            using (MemoryStream output = new MemoryStream()) {
                GZipStream gzip = new GZipStream(output,
                                  CompressionMode.Compress, true);
                gzip.Write(source, 0, source.Length);
                gzip.Close();
                return Convert.ToBase64String(output.ToArray());
            }
        }

        string Decompress(string compressedText) {
            var data = Convert.FromBase64String(compressedText);
            using (MemoryStream input = new MemoryStream(data))
            using (GZipStream gzip = new GZipStream(input,
                              CompressionMode.Decompress, true))
            using (MemoryStream output = new MemoryStream()) {
                byte[] buff = new byte[64];
                int read = -1;
                read = gzip.Read(buff, 0, buff.Length);
                while (read > 0) {
                    output.Write(buff, 0, read);
                    read = gzip.Read(buff, 0, buff.Length);
                }
                return Encoding.Default.GetString(output.ToArray());
            }
        }
        #endregion
    }

    [System.Diagnostics.DebuggerStepThrough]
    static class PageGridBindContext {
        static readonly BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Instance;
        static readonly MethodInfo pushDataBindingContextMethod = typeof(Page).GetMethod("PushDataBindingContext", flags);
        static readonly MethodInfo popDataBindingContextMethod = typeof(Page).GetMethod("PopDataBindingContext", flags);

        static internal void PushDataBindingContext(Page page, object item) {
            pushDataBindingContextMethod.Invoke(page, new object[] { item });
        }

        static internal void PopDataBindingContext(Page page) {
            popDataBindingContextMethod.Invoke(page, null);
        }
    }
}
